﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Spanner;

namespace SpannerWebSite
{
    internal partial class RefKO : Reference
    {
        internal static SpannerChapter TheChapter;

        static RefKO()
        {
            var sections = new List<SpannerSection> { };
            var initActs = new List<Act> { };

            Action<string, string, Html> addSection = (title, id, html) => {
                sections.Add(Section(Esc(title), id, html));
            };

            sections.Add(
                Section("ObsVar", "ObsVar",
                    P(Code("ObsVar<Type>(name, value)"), " constructs an observable variable of the ",
                    "given name and type, initialised to the given value. ",
                    "The variable is made a property of the enclosing view or library model. ",
                    "The type argument is optional if the type can be unambiguously inferred from the given value. ",
                    "The name argument is optional, but omitting it will lead to the JavaScript variable having ",
                    "a generated name. ",
                    "If the name is not unique, a distinguishing suffix will be appended."),
                    CodeFigures("ObsVarExample", 
                        () => {
                            // ObsVarExample
                            var u = ObsVar<int[]>("u", new int[] { 1, 1, 2, 3, 5 });
                            var v = ObsVar<string>("v", null);
                            var w = ObsVar("w", true);
                            var x = ObsVar("x", 123);
                            var y = ObsVar("y", 2.718);
                            var z = ObsVar("z", "abc");
                            // END
                            Content = Show(ToString(u) + ToString(v) + ToString(w) + ToString(x) + ToString(y) + ToString(z)); // Ensure vars are included!
                        },
                        skipHtml: true, includeTryItLink: false
                    ),
                    P("Note that array types are treated specially to take advantage of ", Knockout, "'s array ",
                    "observables."),
                    P("Note that observables of type ", Code("int"), " and ", Code("double"),
                    " are treated specially, ",
                    "to ensure that these values are always reported as numbers.  This is useful when, for example, ",
                    "the value is obtained from an ", Code("input"), " element, which always returns a string value.")
                )
            );

            sections.Add(
                Section("NewObsVar", "NewObsVar",
                    P(Code("NewObsVar<Type>(value)"), " dynamically creates a (reference to a) new ",
                    "observable of the given type and value."),
                    CodeFigures("NewObsVarExample",
                        () => {
                            // NewObsVarExample
                            var xs = ObsVar<ObsVarRef<int>[]>("xs", Array<ObsVarRef<int>>());
                            InitAct = Do(
                                Push(xs, NewObsVar<int>(1)),
                                Push(xs, NewObsVar<int>(2)),
                                Push(xs, NewObsVar<int>(3))
                            );
                            // END
                            Content = Show(ToString(xs)); // Ensure vars are included!
                        },
                        skipHtml: true, includeTryItLink: false
                    ),
                    P("XXX Need to ensure Spanner uses Knockout observable array push.")
                        .WithAttr("style", "color: red"),
                    P()
                )
            );

            sections.Add(
                Section("ComputedVar", "ComputedVar",
                    P(Code("ComputedVar<Type>(name, value)"), " dynamically creates a new ",
                    "computed observable of the given type and value with the given name. ",
                    "Any change to an observable in the value will cause the computed observable ",
                    "to be updated accordingly.",
                    "The variable is made a property of the enclosing view or library model. ",
                    "The type argument is optional if the type can be unambiguously inferred from the given value. ",
                    "The name argument is optional, but omitting it will lead to the JavaScript variable having ",
                    "a generated name. ",
                    "If the name is not unique, a distinguishing suffix will be appended."),
                    CodeFigures("ComputedVarExample",
                        () => {
                            // ComputedVarExample
                            var x = ObsVar("x", 1);
                            var y = ComputedVar("y", 10 * x + 3);
                            // END
                            Content = Show(x + y); // Ensure vars are included!
                        },
                        skipHtml: true, includeTryItLink: false
                    ),
                    P()
                )
            );

            sections.Add(
                Section("NewComputedVar", "NewComputedVar",
                    P(Code("NewComputedVar<Type>(value)"), " dynamically creates a (reference to a) new ",
                    "computed observable of the given type and value."),
                    CodeFigures("NewComputedVarExample",
                        () => {
                            // NewComputedVarExample
                            var x = ObsVar("x", 0);
                            var xs = ObsVar<ComputedVarRef<int>[]>("xs", Array<ComputedVarRef<int>>());
                            InitAct = Do(
                                Push(xs, NewComputedVar<int>(x + 1)),
                                Push(xs, NewComputedVar<int>(x + 2)),
                                Push(xs, NewComputedVar<int>(x + 3))
                            );
                            // END
                            Content = Show(ToString(xs)); // Ensure vars are included!
                        },
                        skipHtml: true, includeTryItLink: false
                    ),
                    P()
                )
            );

            sections.Add(
                Section("Subscribe", "Subscribe",
                    P(Code("Subscribe(observableRef, proc)"), " creates a subscription on the observable, ",
                    "triggering the given procedure whenever the observable changes (the new value of the ",
                    "observable is passed to the procedure as its argument)."),
                    CodeFigures("SubscribeExample",
                        () => {
                            // SubscribeExample
                            var x = ObsVar("x", 0);
                            var s = Var("s", Subscribe(x, Proc<int>(newValue => JSAct("/* Do something... */"))));
                            InitAct = Set(x, x + 1); // This will trigger the subscription on x.
                            // END
                            Content = Show(ToString(s)); // Ensure vars are included!
                        },
                        skipHtml: true, includeTryItLink: false
                    ),
                    P("The handle ", Code("s"), " returned from the observable can be used to cancel the ",
                    "subscription by invoking ", Code("Dispose(s)"), ". ",
                    "If the subscription handle is not required, one can simply create it via ",
                    Code("Do(Subscribe(x, ...))"), " using the unary ", Code("Do"), " form to evaluate the ",
                    Code("Subscribe"), " expression purely for its side effect (viz, placing the subscription).")
                )
            );

            sections.Add(
                Section("Peek", "Peek",
                    P(Code("Peek(observableRef)"), " returns the current value of the observable without ",
                    "placing any dependency on that observable.  This is useful, for example, when creating ",
                    "computed observables which do not depend on changes to certain observables used to compute ",
                    "them."),
                    CodeFigures("PeekExample",
                        () => {
                            // PeekExample
                            var x = ObsVar("x", 42);
                            var y = ObsVar("y", 69);
                            var z = ComputedVar(x + Peek(y)); // Changes to x (but not y) will update z.
                            // END
                            Content = Show(ToString(z)); // Ensure vars are included!
                        },
                        skipHtml: true, includeTryItLink: false
                    )
                )
            );

            sections.Add(
                Section("KoForEach (Virtual Binding)", "KoForEachVirtual",
                    P(Code("KoForEach(xs, x => htmlForX)"), " corresponds to the ", Knockout, " ",
                    Code("<!-- ko foreach: xs -->"), " virtual binding."),
                    CodeFigures("KoForEachVirtualExample",
                        () => {
                            // KoForEachVirtualExample
                            var xs = ObsVar("xs", Array<int>(1, 2, 3));
                            Content = KoForEach(xs, x => P(Show(x)));
                            // END
                        }
                    ),
                    P()
                )
            );

            sections.Add(
                Section("KoForEach (Element Binding)", "KoForEachElement",
                    P(Code("htmlElt.KoForEach(xs, x => htmlForX)"), " corresponds to the ", 
                    KnockoutBindingDocs("foreach"), "."),
                    CodeFigures("KoForEachElementExample",
                        () => {
                            // KoForEachElementExample
                            var xs = ObsVar("xs", Array<int>(1, 2, 3));
                            Content = UL().KoForEach(xs, x => LI(Show(x)));
                            // END
                        }
                    ),
                    P("Note that you can nest for-each bindings in ", Spanner, " much more conveniently ",
                    "than in vanilla ", Knockout, ".  For example: "),
                    CodeFigures("NestedKoForEachExample",
                        () => {
                            // NestedKoForEachExample
                            var xs = ObsVar("xs", Array("a", "b", "c"));
                            var ys = ObsVar("ys", Array(1, 2, 3));
                            Content =
                                UL().KoForEach(xs, x =>
                                    KoForEach(ys, y =>
                                        LI(Show(x), Show(y))
                                    )
                                );
                            // END
                        }
                    ),
                    P()
                )
            );

            sections.Add(
                Section("KoVisible", "KoVisible",
                    P(Code("htmlElt.Visible(cond)"), " corresponds to the ", 
                    KnockoutBindingDocs("visible"), "."),
                    CodeFigures("KoVisibleExample",
                        () => {
                            // KoVisibleExample
                            var x = ObsVar("x", true);
                            Content = P("Here I am.").KoVisible(x);
                            // END
                        }
                    ),
                    P()
                )
            );

            sections.Add(
                Section("KoText", "KoText",
                    P(Code("htmlElt.KoText(x)"), " corresponds to the ", 
                    KnockoutBindingDocs("visible"), ".  ",
                    "It works for string, int, and double valued expressions."),
                    CodeFigures("KoTextExample",
                        () => {
                            // KoTextExample
                            var x = ObsVar("x", 3.14159);
                            Content = P().KoText(x);
                            // END
                        }
                    ),
                    P()
                )
            );

            sections.Add(
                Section("Show", "Show",
                    P(Code("Show(x)"), " is shorthand for ", Code("Span().KoText(x)")),
                    CodeFigures("KoShowExample",
                        () => {
                            // KoShowExample
                            var x = ObsVar("x", "Hello, World!");
                            Content = P(Show(x));
                            // END
                        }
                    ),
                    P()
                )
            );

            sections.Add(
                Section("KoHtml", "KoHtml",
                    P(Code("htmlElt.KoHtml(x)"), " corresponds to the ",
                    KnockoutBindingDocs("html"), ".  ",
                    "It takes a string valued expression interpreted as literal HTML."),
                    CodeFigures("KoHtmlExample",
                        () => {
                            // KoHtmlExample
                            var x = ObsVar("x", "<span>Lawks!</span>");
                            Content = P().KoHtml(x);
                            // END
                        }
                    ),
                    P()
                )
            );

            sections.Add(
                Section("KoCss", "KoCss",
                    P(Code("htmlElt.KoCss(x)"), " corresponds to the ", 
                    KnockoutBindingDocs("css"),
                    " where ", Code("x"), " evaluates to a space-separated list of ",
                    "CSS classes."),
                    CodeFigures("KoCssListExample",
                        () => {
                            // KoCssListExample
                            var x = ObsVar("x", "short shiny black");
                            Content = P("Nice dress").KoCss(x);
                            // END
                        }
                    ),
                    P("Alternatively, ", Code("htmlElt.KoCss(CssClassCondition(className, cond), ...)"), " ",
                    "corresponds to the ", Knockout, " ", Code("css"), " binding taking a list of CSS class/",
                    "condition pairs."),
                    CodeFigures("KoCssCondExample",
                        () => {
                            // KoCssCondExample
                            var score = ObsVar("score", 88);
                            Content = P(Show(score).KoCss(
                                CssClassCondition("dismal", score < 30), 
                                CssClassCondition("terrific", 90 < score)
                            ));
                            // END
                        }
                    ),
                    P()
                )
            );

            sections.Add(
                Section("KoStyle", "KoStyle",
                    P(Code("htmlElt.KoStyle(CssStyleValue(styleName, value)"), " corresponds to the ",
                    KnockoutBindingDocs("style"), "."),
                    CodeFigures("KoStyleExample",
                        () =>
                        {
                            // KoStyleExample
                            var color = ObsVar("color", "red");
                            var weight = ObsVar("weight", "bold");
                            Content = P("Anger").KoStyle(
                                CssStyleValue("color", color),
                                CssStyleValue("font-weight", weight)
                            );
                            // END
                        }
                    ),
                    P("(Observe that ", Spanner, " knows when to quote CSS class and style names.)")
                )
            );

            sections.Add(
                Section("KoAttr", "KoAttr",
                    P(Code("htmlElt.KoAttr(AttrValue(styleName, value)"), " corresponds to the ", 
                    KnockoutBindingDocs("attr"), "."),
                    CodeFigures("KoAttrExample",
                        () =>
                        {
                            // KoAttrExample
                            var min = ObsVar("min", 1);
                            var max = ObsVar("max", 10);
                            var x = ObsVar("x", min); // XXX This SHOULD generate a spanner.ObsInt!
                            Content = P(Input().KoValue(x).KoAttr(
                                AttrValue<string>("type", "number"),
                                AttrValue("min", min),
                                AttrValue("max", max)
                            ));
                            // END
                        }
                    ),
                    P()
                )
            );

            sections.Add(
                Section("KoIf", "KoIf",
                    P(Code("htmlElt.If(cond)"), " corresponds to the ",
                    KnockoutBindingDocs("if"), "."),
                    CodeFigures("KoIfExample",
                        () => {
                            // KoIfExample
                            var x = ObsVar("x", true);
                            Content = P("I think, therefore I am.").KoIf(x);
                            // END
                        }
                    ),
                    P("This is also supported as a Knockout 'virtual' binding."),
                    CodeFigures("KoIfVirtualExample",
                        () => {
                            // KoIfVirtualExample
                            var x = ObsVar("x", true);
                            Content = KoIf(x, P("I think, therefore I am."));
                            // END
                        }
                    ),
                    P()
                )
            );

            sections.Add(
                Section("KoIfNot", "KoIfNot",
                    P(Code("htmlElt.IfNot(cond)"), " corresponds to the ",
                    KnockoutBindingDocs("ifnot"), "."),
                    CodeFigures("KoIfNotExample",
                        () => {
                            // KoIfNotExample
                            var x = ObsVar("x", true);
                            Content = P("I think not, therefore I am not.").KoIfNot(x);
                            // END
                        }
                    ),
                    P("This is also supported as a Knockout 'virtual' binding."),
                    CodeFigures("KoIfNotVirtualExample",
                        () => {
                            // KoIfNotVirtualExample
                            var x = ObsVar("x", true);
                            Content = KoIfNot(x, P("I think not, therefore I am not."));
                            // END
                        }
                    ),
                    P()
                )
            );

            sections.Add(
                Section("KoIfTruthy", "KoIfTruthy",
                    P(Code("htmlElt.IfTruthy(expr)"), " corresponds to the ",
                    KnockoutBindingDocs("if"),
                    ", but follows JavaScripts exciting rule of ", Em("truthiness"), "."),
                    CodeFigures("KoIfTruthyExample",
                        () => {
                            // KoIfTruthyExample
                            var x = ObsVar("x", "truthy value");
                            Content = P("I think vaguely, therefore I am vague.").KoIfTruthy(x);
                            // END
                        }
                    ),
                    P("This is also supported as a Knockout 'virtual' binding."),
                    CodeFigures("KoIfTruthyVirtualExample",
                        () => {
                            // KoIfTruthyVirtualExample
                            var x = ObsVar("x", "truthy value");
                            Content = KoIfTruthy(x, P("I think vaguely, therefore I am vague."));
                            // END
                        }
                    ),
                    P()
                )
            );

            sections.Add(
                Section("KoIfFalsy", "KoIfFalsy",
                    P(Code("htmlElt.IfFalsy(expr)"), " corresponds to the ",
                    KnockoutBindingDocs("ifnot"),
                    ", but follows JavaScripts exciting rule of ", Em("falsiness"), "."),
                    CodeFigures("KoIfFalsyExample",
                        () => {
                            // KoIfFalsyExample
                            var x = ObsVar("x", 0);
                            Content = P("I think wrongly, therefore I am wrong.").KoIfFalsy(x);
                            // END
                        }
                    ),
                    P("This is also supported as a Knockout 'virtual' binding."),
                    CodeFigures("KoIfFalsyVirtualExample",
                        () => {
                            // KoIfFalsyVirtualExample
                            var x = ObsVar("x", 0);
                            Content = KoIfFalsy(x, P("I think wrongly, therefore I am wrong."));
                            // END
                        }
                    ),
                    P()
                )
            );

            sections.Add(
                Section("KoClick", "KoClick",
                    P(Code("htmlElt.KoClick(action)"), " corresponds to the ",
                    KnockoutBindingDocs("click"), ".  Its argument may be "),
                    UL(
                        LI("an action, which will be converted into an in-line function;"),
                        LI("a proc, either in-line or named; or"),
                        LI("a bool valued func (presumably with a side effect), either in-line or named, ",
                        "returning ", Code("false"), " from which will cause the default click action ",
                        "(e.g., for ", Code("A"), " elements) to be suppressed;"),
                        LI("either of the previous two options, except taking a dummy argument and an ",
                        "argument corresponding to the click event (only the former is illustrated below).")
                    ),
                    CodeFigures("KoClickExample",
                        () =>
                        {
                            // KoClickExample
                            var x = ObsVar("x", "unclicked");
                            var plainAction = Set(x, "clicked on plain action");
                            var inlineProc = Proc(Set(x, "clicked on proc"));
                            var namedFn = Function("namedFn", () => ActExpr<bool>(
                                Set(x, "clicked on func"),
                                false // Returning false inhibits the default click action.
                            ));
                            var eventHandler = Procedure<DummyType, DummyType>("eventHandler", (data, evt) =>
                                Do(
                                    Set(x, "clicked on handler"),
                                    Alert(JSExpr<string>("#.type", evt))
                                )
                            );
                            Content = Group(
                                Button("Plain action").KoClick(plainAction),
                                Button("Proc").KoClick(inlineProc),
                                A("Func").WithAttr("href", "#").KoClick(namedFn),
                                Button("Handler").KoClick(eventHandler),
                                P("Status: ", Show(x))
                            );
                            // END
                        }
                    ),
                    P()
                )
            );

            sections.Add(
                Section("KoEvent", "KoEvent",
                    P(Code("htmlElt.KoEvent(DomEventHandler(eventID, handler), ...)"), " corresponds to the ",
                    KnockoutBindingDocs("event"), ".  ",
                    "Multiple ", Code("DomEventHandler"), " arguments ",
                    "may be provided.  Each ", Code("DomEventHandler"), " takes an event name and an event ",
                    "handler.  An event handler is one of the following: "),
                    UL(
                        LI("an action, which will be converted into an in-line function;"),
                        LI("a proc, either in-line or named; or"),
                        LI("a bool valued func (presumably with a side effect), either in-line or named, ",
                        "returning ", Code("false"), " from which will cause the default click action ",
                        "(e.g., for ", Code("A"), " elements) to be suppressed;"),
                        LI("either of the previous two options, except taking a dummy argument and an ",
                        "argument corresponding to the event (only the former is illustrated below).")
                    ),
                    CodeFigures("KoEventExample",
                        () =>
                        {
                            // KoEventExample
                            var x = ObsVar("x", 0);
                            var y = ObsVar("y", 0);
                            var n = ObsVar("n", 0);
                            var eventHandler = Procedure<DummyType, DummyType>("eventHandler", (data, evt) =>
                                Do(
                                    Set(x, JSExpr<int>("#.x", evt)),
                                    Set(y, JSExpr<int>("#.y", evt)),
                                    IfThen(JSExpr<string>("#.type", evt) == "click", Set(n, n + 1))
                                )
                            );
                            Content = Div()
                                .WithAttr("style", "width: 300px")
                                .WithAttr("style", "height: 300px")
                                .WithAttr("style", "background: yellow")
                                .WithChildren(
                                    P("x = ", Show(x)),
                                    P("y = ", Show(y)),
                                    P("n = ", Show(n))
                                 )
                                .KoEvent(
                                    DomEventHandler(DomEventName.MouseMove, eventHandler),
                                    DomEventHandler(DomEventName.Click, eventHandler)
                                );
                            // END
                        }
                    ),
                    P()
                )
            );

            sections.Add(
                Section("KoNoBubble and KoBubble", "KoNoBubble",
                    P(Code("formHtmlElt.KoBubble(\"eventName\", cond)"), " corresponds to the ",
                    Knockout, " ", A(Code("eventNameBubble: cond")).WithAttr("href", "http://knockoutjs.com/documentation/event-binding.html#note-4-preventing-the-event-from-bubbling"), " binding ",
                    "(", Code("KoNoBubble(\"eventName\")"), " is shorthand for ",
                    Code("KoBubble(\"eventName\", false)"), ")."),
                    CodeFigures("KoNoBubbleExample",
                        () => {
                            // KoNoBubbleExample
                            var x = ObsVar("x", false);
                            Content = Div()
                                .WithAttr("style", "border: 20px solid green")
                                .KoClick(Fn(() => ActExpr<bool>(Alert("Div: click event"), true)))
                                .WithChildren(
                                    Button("Click me!")
                                        .KoClick(Alert("Button: click event"))
                                        .KoBubble(DomEventName.Click, x),
                                    Input().WithAttr("type", "checkbox").KoChecked(x), " bubble button click."
                                 );
                            // END
                        }
                    ),
                    P()
                )
            );

            sections.Add(
                Section("KoSubmit", "KoSubmit",
                    P(Code("formHtmlElt.KoSubmit(handler)"), " corresponds to the ",
                    KnockoutBindingDocs("submit"), "."),
                    CodeFigures("KoSubmitExample",
                        () => {
                            // KoSubmitExample
                            var x = ObsVar("x", "");
                            var submitHandler = Procedure("submitHandler", Alert(x));
                            Content = Form(
                                Input().KoValue(x),
                                Button("Submit").WithAttr("type", "submit")
                            ).KoSubmit(submitHandler);
                            // END
                        }
                    ),
                    P()
                )
            );

            sections.Add(
                Section("KoEnable and KoEnabled", "KoEnable",
                    P(Code("htmlElt.KoEnable(handler)"), " corresponds to the ",
                    KnockoutBindingDocs("enable"), "."),
                    CodeFigures("KoEnableExample",
                        () => {
                            // KoEnableExample
                            var x = ObsVar("x", "");
                            var y = ObsVar("x", true);
                            Content = Div(
                                Input().KoValue(x).KoEnabled(y),
                                Input().WithAttr("type", "checkbox").KoChecked(y), "enable input"
                            );
                            // END
                        }
                    ),
                    P("Note that ", Spanner, " also provides a synonym, ", Code("KoEnabled"), " to suit ",
                    "those of us who prefer their code to have a more declarative flavour."),
                    P()
                )
            );

            sections.Add(
                Section("KoDisable and KoDisabled", "KoDisable",
                    P(Code("htmlElt.KoDisable(handler)"), " corresponds to the ",
                    KnockoutBindingDocs("disable"), "."),
                    CodeFigures("KoDisableExample",
                        () => {
                            // KoDisableExample
                            var x = ObsVar("x", "");
                            var y = ObsVar("x", true);
                            Content = Div(
                                Input().KoValue(x).KoDisabled(y),
                                Input().WithAttr("type", "checkbox").KoChecked(y), "disable input"
                            );
                            // END
                        }
                    ),
                    P("Note that ", Spanner, " also provides a synonym, ", Code("KoDisabled"), " to suit ",
                    "those of us who prefer their code to have a more declarative flavour."),
                    P()
                )
            );

            sections.Add(
                Section("KoValue", "KoValue",
                    P(Code("htmlElt.KoValue(obsVarRef)"), " corresponds to the ",
                    KnockoutBindingDocs("value"), "."),
                    CodeFigures("KoValueExample",
                        () => {
                            // KoValueExample
                            var x = ObsVar("x", "");
                            Content = Div(
                                Input().KoValue(x),
                                P("You input '", Show(x), "'")
                            );
                            // END
                        }
                    ),
                    P()
                )
            );

            sections.Add(
                Section("KoValueUpdate", "KoValueUpdate",
                    P(Code("htmlElt.KoValueUpdate(obsVarRef)"), " corresponds to the ",
                    KnockoutBindingDocs("value"), "."),
                    CodeFigures("KoValueUpdateExample",
                        () => {
                            // KoValueUpdateExample
                            var x = ObsVar("x", "");
                            Content = Div(
                                P(Input().KoValue(x), " (no value update binding)"),
                                P(Input().KoValue(x).KoValueUpdate(KoValueUpdateOn.KeyUp),
                                    " (value update on key up)"),
                                P("You input '", Show(x), "'")
                            );
                            // END
                        }
                    ),
                    P()
                )
            );

            sections.Add(
                Section("KoHasFocus", "KoHasFocus",
                    P(Code("htmlElt.KoHasFocus(obsVarRef)"), " corresponds to the ",
                    KnockoutBindingDocs("hasFocus"), "."),
                    CodeFigures("KoHasFocusExample",
                        () => {
                            // KoHasFocusExample
                            var x = ObsVar("x", true);
                            Content = Div(
                                Input().KoHasFocus(x),
                                Input().WithAttr("type", "checkbox").KoChecked(x), "focus input"
                            );
                            // END
                        }
                    ),
                    P()
                )
            );

            sections.Add(
                Section("KoChecked", "KoChecked",
                    P(Code("htmlElt.KoChecked(obsVarRef)"), " corresponds to the ",
                    KnockoutBindingDocs("checked"), ", used for checkbox and radio button inputs."),
                    CodeFigures("KoCheckedExample",
                        () => {
                            // KoCheckedExample
                            var x = ObsVar("x", true);
                            Content = Div(
                                Input().WithAttr("type", "checkbox").KoChecked(x),
                                Show(Cond<string>(x, "checked", "unchecked"))
                            );
                            // END
                        }
                    ),
                    P()
                )
            );

            sections.Add(
                Section("KoOptions", "KoOptions",
                    P(Code("htmlElt.KoOptions(obsVarRef)"), " corresponds to the ",
                    KnockoutBindingDocs("options"), ", used for select controls."),
                    CodeFigures("KoOptionsExample",
                        () => {
                            // KoOptionsExample
                            var xs = ObsVar("xs", Array("Alice", "Bob", "Cissy"));
                            Content = Div(
                                Select().KoOptions(xs)
                            );
                            // END
                        }
                    ),
                    P()
                )
            );

            sections.Add(
                Section("KoSelectedOptions", "KoSelectedOptions",
                    P(Code("htmlElt.KoOptions(obsVarRef)"), " corresponds to the ",
                    KnockoutBindingDocs("options"), ", used for select controls."),
                    CodeFigures("KoSelectedOptionsExample",
                        () => {
                            // KoSelectedOptionsExample
                            var xs = ObsVar("xs", Array("Alice", "Bob", "Cissy"));
                            var ys = ObsVar("xs", Array("Alice"));
                            Content = Div(
                                Select()
                                    .WithAttr("size", 3)
                                    .WithAttr("multiple", true)
                                    .KoOptions(xs)
                                    .KoSelectedOptions(ys),
                                Div().KoForEach(ys, y => P(Show(y)))
                            );
                            // END
                        }
                    ),
                    P()
                )
            );

            sections.Add(
                Section("KoUniqueName", "KoUniqueName",
                    P(Code("htmlElt.KoUniqueName(cond)"), " corresponds to the ",
                    KnockoutBindingDocs("uniqueName"), " (the condition argument is optional)."),
                    CodeFigures("KoUniqueNameExample",
                        () => {
                            // KoUniqueNameExample
                            Content = Div(Input().KoUniqueName());
                            // END
                        }
                    ),
                    P()
                )
            );

            sections.Add(
                Section("KoTemplate", "KoTemplate",
                    P(Code("htmlElt.KoTemplate(templateModel)"), " corresponds to the ",
                    KnockoutBindingDocs("template"), ".  The user is referred to the ",
                    A("TemplateModels and LibraryModels")
                        .WithAttr("href", "#RefTopLevel-TemplatesAndLibraries")
                        .KoClick(Call(Program.NavigateToChapterSection, "RefTopLevel", "TemplatesAndLibraries")),
                    " section of the ", Em("Reference - Top Level"), " chapter of the documentation."),
                    P()
                )
            );

            TheChapter = CommonUI.Chapter("Reference - Knockout", "RefKO", sections, initActs);
        }
    }
}
